﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Linq.Expressions;
using System.Windows.Forms;

namespace AMM.CustomWinControls {
    public partial class DataGridViewDropDownEx : DropDownControlEx {

        #region Properties

        [Browsable(false)]
        public bool IsFill { get; set; }

        [Browsable(false)]
        public string DisplayMemberEx { get; set; }

        [Browsable(false)]
        public string ValueMemberEx { get; set; }

        [Browsable(false)]
        public object SelectedItemEx { get; set; }

        [Browsable(false)]
        public object SelectedValueEx {
            get { return this._selectedValueEx; }
            set {
                if (value != null) {
                    object objInstance = this._pageDataSource
                        .FirstOrDefault(x => x.GetType().GetProperty(this.ValueMemberEx).GetValue(x, null).Equals(value));
                    if (objInstance != null) {
                        this.SelectedItemEx = objInstance;
                        this.ValueEx = objInstance.GetType().GetProperty(this.DisplayMemberEx).GetValue(objInstance, null);
                    }
                    else {
                        this.SelectedItemEx = null;
                        this.ValueEx = null;
                    }
                }
                else {
                    this.SelectedItemEx = null;
                    this.ValueEx = null;
                }
                this._selectedValueEx = value;
                this.Invalidate();
            }
        }

        [Browsable(false)]
        public List<object> DataGridViewSourceEx {
            get { return this._pageDataSource; }
        }

        [Browsable(false)]
        public int PageSizeEx {
            get { return this._pageSize; }
            set {
                this._pageSize = value;
                if (this._pageSize == 0) { this._pageSize = 20; }
            }
        }

        [Browsable(false)]
        public int WidthEx {
            get { return this._width; }
            set {
                this._width = value;
                if (this._width < this.Width) { this._width = this.Width; }
            }
        }

        [Browsable(false)]
        public int HeightEx {
            get { return this._height; }
            set {
                this._height = value;
                if (this._height < this.Height) { this._height = this.Height; }
            }
        }

        #endregion

        public event EventHandler ValueChanged;

        private int _height = 220, _width = 200;
        private int _pageIndex, _pageCount, _pageSize;
        private object _selectedValueEx;
        private List<object> _pageDataSource, _datasource;
        private List<string> _showColumnNameList;

        public override string Text {
            get { return this.ValueEx == null ? string.Empty : this.ValueEx.ToString(); }
            set { throw new Exception("Prohibiting set operation"); }
        }

        public DataGridViewDropDownEx() {
            this.InitializeComponent();
            if (this.IsInDesignModeEx) { this.Size = this.AnchorSizeEx; }
            this.dgvList.AlternatingRowsDefaultCellStyle.BackColor = Color.LightGray;
        }

        public void DataBind<TViewModel, TViewModelProperties>(TViewModel viewModel,
            Expression<Func<TViewModel, TViewModelProperties>> viewModelAccessor, List<TViewModel> datasoruce) {
            if (viewModelAccessor.NodeType != ExpressionType.Lambda) {
                throw new ArgumentNullException("The viewModelAccessor is not a valid lambda expression.");
            }
            this._datasource = this._pageDataSource = datasoruce.Cast<object>().ToList();
            this.dgvList.DataGridViewModelType = typeof(TViewModel);

            LambdaExpression lambdaExpression = ((LambdaExpression)viewModelAccessor);
            if (lambdaExpression.Body.NodeType == ExpressionType.MemberAccess) {
                MemberExpression memberExpression = (MemberExpression)lambdaExpression.Body;
                this._showColumnNameList.Add(memberExpression.Member.Name);
            }
            if (lambdaExpression.Body.NodeType == ExpressionType.New) {
                NewExpression newExpression = (NewExpression)lambdaExpression.Body;
                newExpression.Members.ToList().ForEach(x => { this._showColumnNameList.Add(x.Name); });
            }

            this.BindDataGridView();
            this.InitializeSelf();
        }

        protected void DataBind(Type viewModelType, List<string> showColumnNames, List<object> datasoruce) {
            this._showColumnNameList = showColumnNames;
            this._datasource = this._pageDataSource = datasoruce.Cast<object>().ToList();
            this.dgvList.DataGridViewModelType = viewModelType;

            this.BindDataGridView();
            this.InitializeSelf();
        }

        protected virtual void OnValueChanged(EventArgs eventargs) {
            if (this.ValueChanged != null) {
                this.ValueChanged(this, eventargs);
            }
        }

        private void DataGridViewDropDownEx_Load(object sender, EventArgs e) {
            this._pageIndex = this._pageCount = 1;
            this._showColumnNameList = new List<string>();

            this.IsFill = true;
            this.DropDownShowed += new EventHandler(DataGridViewDropDownEx_DropDownShowed);
        }

        private void DataGridViewDropDownEx_DropDownShowed(object sender, EventArgs e) {
            if (this.SelectedItemEx != null) {
                int selectedIndex = this._pageDataSource.FindIndex(x => x == this.SelectedItemEx);
                this._pageIndex = ((selectedIndex + 1) / this._pageSize) + ((selectedIndex + 1) % this._pageSize > 0 ? 1 : 0);
                this._pageIndex = this._pageIndex <= 0 ? 1 : this._pageIndex;

                this.BindDataGridView();

                int rowIndex = selectedIndex - (this._pageIndex - 1) * this._pageSize;
                if (rowIndex >= 0 && selectedIndex >= 0) {
                    if (this.dgvList.RowCount > 0) {
                        this.dgvList.CurrentCell = this.dgvList.Rows[rowIndex].Cells[this.DisplayMemberEx];
                    }
                }
            }
        }

        private void InitializeSelf() {
            this.pnlContainer.Width = this._width - 2;
            this.pnlContainer.Height = this._height;

            this.InitializeDropDownEx(this.pnlContainer);
        }

        private void BindDataGridView() {
            if (this._pageDataSource != null) {
                this._pageCount = this._pageDataSource.Count / this._pageSize + (this._pageDataSource.Count % this._pageSize > 0 ? 1 : 0);
                this._pageIndex = this._pageIndex > this._pageCount ? this._pageCount : this._pageIndex;

                List<object> pageList = this._pageDataSource
                    .Skip((this._pageIndex - 1) * this._pageSize)
                    .Take(this._pageSize)
                    .ToList();
                this.dgvList.DataSource = new SortableBindingList<object>(pageList);
                this.dgvList.Refresh();
                this.dgvList.ClearSelection();
                this.dgvList.Focus();
                this.HiddenColumn();
            }

            this.lbTotalPages.Text = string.Format("/ {0}", this._pageCount);
            this.tbPage.Text = this._pageIndex.ToString();
            this.btnFirst.Enabled = this._pageIndex != 1;
            this.btnPrevious.Enabled = this._pageIndex > 1;
            this.btnNext.Enabled = this._pageIndex < this._pageCount;
            this.btnLast.Enabled = this._pageIndex != this._pageCount;
        }

        private void HiddenColumn() {
            this.dgvList.Columns.Cast<DataGridViewColumn>().ToList().ForEach(x => {
                x.Visible = this._showColumnNameList.Contains(x.Name);
            });
            if (this.IsFill) {
                foreach (DataGridViewColumn column in this.dgvList.Columns) {
                    if (column.Visible) {
                        column.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                    }
                }
            }
        }

        private void pageButton_Click(object sender, EventArgs e) {
            Button btn = (Button)sender;
            switch (btn.Name) {
                case "btnFirst":
                    this._pageIndex = 1;
                    break;
                case "btnPrevious":
                    this._pageIndex -= 1;
                    break;
                case "btnNext":
                    this._pageIndex += 1;
                    break;
                case "btnLast":
                    this._pageIndex = this._pageCount;
                    break;
            }
            this.btnSearch_Click(this, EventArgs.Empty);
        }

        private void tb_KeyDown(object sender, KeyEventArgs e) {
            TextBox tb = (TextBox)sender;
            if (e.KeyCode == Keys.Enter) {
                if (tb == this.tbPage) {
                    int.TryParse(this.tbPage.Text, out this._pageIndex);
                }
                this.btnSearch_Click(this, EventArgs.Empty);
            }
        }

        private void btnSearch_Click(object sender, EventArgs e) {
            string searchText = this.tbSearchText.Text;
            if (!string.IsNullOrEmpty(searchText)) {
                this._pageDataSource = this._datasource.Where(x =>
                    this._showColumnNameList.Any(y =>
                        (x.GetType().GetProperty(y).GetValue(x, null) ?? string.Empty)
                            .ToString().ToUpper().Contains(searchText.Trim().ToUpper()))).ToList();
            }
            else {
                this._pageDataSource = this._datasource;
            }
            this.BindDataGridView();
        }

        private void btnReset_Click(object sender, EventArgs e) {
            this.CloseDropDownEx();
            this.SelectedValueEx = null;
            this.ValueEx = null;
            this.SelectedItemEx = null;

            this.tbSearchText.Clear();
            this.btnSearch_Click(this, EventArgs.Empty);
        }

        private void dvList_CellClick(object sender, DataGridViewCellEventArgs e) {
            if (e.RowIndex >= 0) {
                this.SelectedItemEx = this.dgvList.Rows[e.RowIndex].DataBoundItem;
                object cellDisplayMember = this.dgvList.Rows[e.RowIndex].Cells[this.DisplayMemberEx].Value;
                object cellValueMember = this.dgvList.Rows[e.RowIndex].Cells[this.ValueMemberEx].Value;
                if (cellValueMember != null) {
                    this._selectedValueEx = cellValueMember.ToString();
                }
                if (cellDisplayMember != null) {
                    this.ValueEx = cellDisplayMember.ToString();
                }
                this.OnValueChanged(EventArgs.Empty);
                this.CloseDropDownEx();
            }
        }
    }
}
